#include "scheduler.h"


Scheduler_t Scheduler;

Scheduler_t::Scheduler_t()
{
	m_active = false;
	hthread = NULL;
	cycles = 0;
	hbgevent = CreateEvent(NULL, TRUE, TRUE, NULL);
	if (hbgevent == NULL) {
		LogFatal("failed to create event object");
	}
}

bool Scheduler_t::Start()
{
	if (m_active) {
		LogError("scheduler already running");
		return false;
	}
	LogInfo("scheduler is starting");
	m_active = true;
	cycles = 0;
	Work();
	LogInfo("scheduler has terminated");
	return true;
}

bool Scheduler_t::Stop()
{
	if (!m_active) {
		LogError("scheduler not running");
		return false;
	}
	m_active = false;
	LogInfo("scheduler is stopping");
	return true;
}

bool Scheduler_t::IsActive() const
{
	return m_active;
}

bool Scheduler_t::Register(schedproc_t func, void *arg)
{
	return Register(func, arg, DefaultInterval, NULL);
}

bool Scheduler_t::Register(schedproc_t func, void *arg, unsigned int interval)
{
	return Register(func, arg, interval, "<noname>");
}

bool Scheduler_t::Register(schedproc_t func, void *arg, const char *tagline)
{
	return Register(func, arg, DefaultInterval, tagline);
}

bool Scheduler_t::Register(schedproc_t func, void *arg, unsigned int interval, const char *tagline)
{
	SchedulerEntry se;
	
	se.proc = func;
	se.arg = arg;
	se.tagline = tagline;
	se.interval = interval;
	se.ticks = interval;
	return queue.Push(se);
}

bool Scheduler_t::RegisterBg(schedproc_t func, void *arg)
{
	return RegisterBg(func, arg, "<noname>");
}

bool Scheduler_t::RegisterBg(schedproc_t func, void *arg, const char *tagline)
{
	SchedulerEntry se;
	
	se.proc = func;
	se.arg = arg;
	se.tagline = tagline;
	se.interval = 0;
	se.ticks = 0;
	if (!queue.Push(se)) {
		return false;
	}
	SetEvent(hbgevent);
	return true;
}

void Scheduler_t::Work()
{
	SchedulerEntry se;
	
	// start bg worker thread
	hthread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkBg, this, 0, NULL);
	if (hthread == NULL) {
		LogFatal("failed to create bg thread");
	}
	
	// start local scheduler
	while (m_active) {
		if (!queue.Pop(se)) {
			LogFatal("scheduler queue is empty");
		}
		if (se.ticks > 0) {
			se.ticks--;
			queue.Push(se);
		}
		else {
			if (se.proc(se.arg)) {
				se.ticks = se.interval;
				queue.Push(se);
			}
		}
		cycles++;
	}
	
	// wait for bg thread to quit
	SetEvent(hbgevent);
	if (WaitForSingleObject(hthread, WaitBgThreadTimeout) != WAIT_OBJECT_0) {
		LogInfo("forcibly stopping bg thread");
		TerminateThread(hthread, -1);
	}
}


#include <stdio.h>

DWORD WINAPI Scheduler_t::WorkBg(Scheduler_t& self)
{
	SchedulerEntry se;
	
	while (self.m_active) {
		if (self.bgqueue.IsEmpty()) {
			WaitForSingleObject(self.hbgevent, INFINITE);
			ResetEvent(self.hbgevent);
		}
		else {
			self.bgqueue.Pop(se);
			if (se.proc(se.arg)) {
				self.bgqueue.Push(se);
			}
		}
	}
	
	return 0;
}




